#include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    #include <openssl/ssl.h>
    #include <openssl/err.h>
    #include <openssl/rsa.h>
    #include <openssl/pem.h>
    #include <openssl/err.h>
    #include <openssl/rand.h>

    #define PORT 6999
    #define HOST "localhost"

    #define PUBLIC_KEYFILE "/home/harsha/.ecmfs_server/my_enc_pub_key.pem"
    #define CERT_FILE "server.crt"

    #define PRIVATE_KEYFILE "/home/harsha/.ecmfs_server/my_enc_priv_key.pem"

    void run_client(void);

    EVP_CIPHER_CTX en_ctx;
    EVP_CIPHER_CTX de_ctx;

    typedef struct {
    int keylen;
    unsigned char key[512];
    unsigned char iv[EVP_MAX_IV_LENGTH];
     } keys_t;

     unsigned char msg[1024];
     unsigned char enc_buf[1024];
     unsigned char de_buf[1024];


     int main(int argc, char **argv)
     {

     // Init malloc, free etc for OpenSSL's CRYPTO_malloc_init();
     ERR_load_BIO_strings();    
     OpenSSL_add_all_algorithms();
     run_client();

     return 0;  

     }

     int encrypt_with_cert(unsigned char **secret_key, int *keylen, 
                           unsigned char **out_iv, 
                   unsigned char *data, int *datalen)
    {
    FILE *fp;
    //X509 *x509;
    EVP_PKEY *pkey;

    unsigned char *ek;
    int eklen;
    int retval = 0;
    unsigned char iv[EVP_MAX_IV_LENGTH];
    int i=0;

#if 0
        RAND_pseudo_bytes(iv, EVP_MAX_IV_LENGTH);

    fp = fopen(CERT_FILE, "r");
    if (!fp) {
      fprintf(stderr, "Cannot open certificate file\n");
      goto out;
    }

    x509 = PEM_read_X509(fp, NULL, 0, NULL);
    if (x509 == NULL) {
      ERR_print_errors_fp(stderr);
      goto out;
    }
    fclose(fp);

    pkey = X509_extract_key(x509);

    X509_free(x509);
#endif

    int tot_len = 0;
    read_rsa_pub_key(PUBLIC_KEYFILE, &pkey);
    if (pkey == NULL) {
      ERR_print_errors_fp(stderr);
    }

    RSA *trsa = EVP_PKEY_get1_RSA(pkey);
    unsigned char *to = (unsigned char *) malloc(RSA_size(trsa));

    tot_len = RSA_public_encrypt(*datalen, data, 
			to, trsa,RSA_PKCS1_PADDING);
    RSA_free(trsa);
#if 0
    EVP_CIPHER_CTX_init(&en_ctx);
    ek = malloc(EVP_PKEY_size(pkey));

    if (!EVP_SealInit(&en_ctx, EVP_aes_256_cbc(), &ek, 
                          &eklen, iv, &pkey, 1)) {
      fprintf(stderr, "EVP_SealInit failed\n");
      retval = 3;
      goto out_free;
    }

     int buf_len = 0;
     int tot_len = 0;

     printf("Encoding string: {%s}, len: %d\n", data, *datalen);
     memset(enc_buf, 0 , 1024);

     //EVP_SealInit(&en_ctx, NULL, NULL, NULL, NULL, NULL, 1);
     EVP_SealUpdate(&en_ctx, enc_buf, &buf_len, data, *datalen);
     tot_len += buf_len;

     EVP_SealFinal(&en_ctx, enc_buf+buf_len, &buf_len);
     tot_len += buf_len;

     *(enc_buf + tot_len) = '\0';
     *datalen = tot_len;
#endif
     printf("Encoded length: %d\n", tot_len);
     printf("Encoded message: {");
     for (i=0; i< tot_len; i++) {
    		printf("%02x", to[i]);
     }
     printf("}\n");

     goto out;

      out_free:
     EVP_PKEY_free(pkey);
     //free(ek);

      out:
    *secret_key = to;
    *keylen = tot_len;
    //*out_iv = iv;
    return retval;
    }

    void decrypt_message(unsigned char *secret_key, int keylen, 
                         unsigned char *iv, int buflen)
    {
    FILE *privkey;

    int retval = 0;
    int klen=0;

    int tot_len = 0;
    EVP_PKEY *pkey;
    int i = 0;

    privkey = fopen(PRIVATE_KEYFILE, "r");
    if (!privkey) {
      fprintf(stderr, "Error opening private key\n");
        }   

    pkey = PEM_read_PrivateKey(privkey, NULL, 0 , NULL);
    if (!pkey) {
      fprintf(stderr, "Error loading Server's RSA private key\n");
      retval = 2;
      goto out; 
    }


    if(keylen != EVP_PKEY_size(pkey)) {
       EVP_PKEY_free(pkey);
       fprintf(stderr, "keylength mismatch!\n");
       retval = 2;
       goto out;
    }       

    klen = EVP_OpenInit(&de_ctx, EVP_aes_256_cbc(), secret_key, keylen, iv, pkey);
    if (klen == 0) {
      fprintf(stderr, "EVP_OpenInit failed\n");
    }

    int de_len = 0;
    tot_len = 0;

    printf("\n\nNow decoding..\n");
    printf("Encoded length: %d\n", buflen);
    printf("Encoded message: {");
    for (i=0; i< buflen; i++) {
      printf("%02x", enc_buf[i]);
    }
    printf("}\n");


    EVP_OpenInit(&de_ctx, NULL, NULL, keylen, NULL, NULL);
    EVP_OpenUpdate(&de_ctx, de_buf, &de_len, enc_buf, buflen);
    tot_len += de_len;


    EVP_OpenFinal(&de_ctx, de_buf+tot_len, &de_len);
    tot_len += de_len;
    *(de_buf+tot_len) = '\0';


    printf("Decoded length: %d,  String: %s\n", tot_len, de_buf);

     out:
    fclose(privkey);
return;
    } 

   void run_client()
   {

    unsigned char *secret_key = NULL;
    int keylen;
    unsigned char *iv = NULL;
    int data_len = 0;
    int retval = 0;

    //strcpy((char *)msg, "abcdefghijklmnopqrstuvwxyz0123456789");
    //strcpy((char *)msg, "this is a secret message\n");


        strcpy((char *)msg, "1234567890");

        data_len = strlen((char *)msg) + 1; 

    retval = encrypt_with_cert(&secret_key, &keylen, &iv, msg, &data_len);
    if (retval != 0) {
    printf("encrypt with certificate failed! retval = %d\n", retval);
    }

    decrypt_message(secret_key, keylen, iv, data_len);

    }


int read_rsa_pub_key(
            char *pub_key_fname,
            EVP_PKEY **pPubKey)
{
    FILE*     pFile    = NULL;
    int c;

    *pPubKey = NULL;
    
    if (pub_key_fname) {
        
    pFile = fopen(pub_key_fname,"rt");
    
    if(pFile) {   
        *pPubKey = PEM_read_PUBKEY(pFile,NULL,NULL,NULL);
        if (*pPubKey)
        {
            fprintf(stderr,"Public key read.\n");
        }
        else
        {
            fprintf(stderr,"Cannot read public key.\n");
            return -1;
        }
        
        fclose(pFile);
        pFile = NULL;
    }
    }
    
    return 0;
}
